package com.hentre.famonitor.common.util.coordinate;

import java.text.DecimalFormat;


/**
 * 
 * @author Yanlun
 * @date 2015年11月4日
 */
public final class CoordinateUtil {

	public static final Double EARTH_RADIUS = 6378137.0D/1000.0;
	public static final Double BASE_DISTANCE_OF_LAT = Math.PI * EARTH_RADIUS / 180.0;//同一经度上,纬度每隔一度相差BASE_DISTANCE_OF_LAT
	public static final DecimalFormat df = new DecimalFormat("###.#####");
	
	/**
	 * 
	 * @param deviceBaseCoordinate 设备坐标
	 * @param targetCorrdinate 火情报警坐标
	 * @return
	 */
	public static Double calcDistance(Coordinate deviceBaseCoordinate, Coordinate targetCorrdinate){
		Double lat0 = toRadians(deviceBaseCoordinate.getLat());
		Double lng0 = toRadians(deviceBaseCoordinate.getLng());
		Double lat1 = toRadians(targetCorrdinate.getLat());
		Double lng1 = toRadians(targetCorrdinate.getLng());
		
		Double temp = Math.sin(lat0) * Math.sin(lat1) + Math.cos(lat0) * Math.cos(lat1) * Math.cos(lng0 - lng1);
		Double distance = Math.acos(temp) * EARTH_RADIUS;
		
		return distance;
	}
	
	/**
	 * 在平面上用三角函数计算
	 * @param zoomDistance 拉近距离（公里数）
	 * @param angle 正北方向顺时针角度
	 * @param deviceBaseCoordinate 设备坐标
	 * @param targetCorrdinate 火情报警坐标
	 * @return
	 */
	public static Coordinate calcZoomCoordinateV0(Double zoomDistance, Double angle, Coordinate deviceBaseCoordinate, Coordinate targetCorrdinate){
		Double distanceOfDevice = calcDistance(deviceBaseCoordinate, targetCorrdinate) - zoomDistance;//单位:km calcDistance([28.00,112.00],[27.9797,111.9891]) - zoomDistance
        if(zoomDistance < 0){//zoomDistance距离设置有误
        	throw new RuntimeException("zoomDistance距离设置有误");
        }else{
        	Double lat0 = deviceBaseCoordinate.getLat();
        	Double lng0 = deviceBaseCoordinate.getLng();
        	
        	Double lat = lat0 + (distanceOfDevice * Math.cos(toRadians(angle)) / BASE_DISTANCE_OF_LAT);
        	Double lng = lng0 + (distanceOfDevice * Math.sin(toRadians(angle)) / getBaseDistanceOfLng(lat0));
        	
        	return new Coordinate(lat, lng);
        }
	}
	
	/**
	 * 精度较高
	 * @param zoomDistance 拉近距离（公里数）
	 * @param angle 正北方向顺时针角度
	 * @param deviceBaseCoordinate 设备坐标
	 * @param targetCorrdinate 火情报警坐标
	 * @return
	 */
	public static Coordinate calcZoomCoordinateV1(Double zoomDistance, Double angle, Coordinate deviceBaseCoordinate, Coordinate targetCorrdinate){
	 	zoomDistance = calcDistance(deviceBaseCoordinate, targetCorrdinate) - zoomDistance;
        if(zoomDistance < 0){//zoomDistance距离设置有误
        	throw new RuntimeException("zoomDistance距离设置有误");
        }else{
        	double lat2 = 0.0;
        	double lon2 = 0.0;
        	double R = EARTH_RADIUS;
        	double φ1 = toRadians(deviceBaseCoordinate.getLat());
        	double λ1 = toRadians(deviceBaseCoordinate.getLng());
        	double θ = toRadians(angle);
        	
        	double φ2 = Math.asin(Math.sin(φ1) * Math.cos(zoomDistance / R) + Math.cos(φ1) * Math.sin(zoomDistance / R) * Math.cos(θ));
        	
        	double λ2 = λ1 + Math.atan2(Math.sin(θ) * Math.sin(zoomDistance / R) * Math.cos(φ1), Math.cos(zoomDistance / R) - Math.sin(φ1) * Math.sin(φ2));
        	λ2 = (λ2 + 3 * Math.PI) % (2 * Math.PI) - Math.PI; // normalise to -180..+180°
        	lat2 = toDegrees(φ2);
        	lon2 = toDegrees(λ2);
        	
        	return new Coordinate(lat2, lon2);
        }
	}
	
	public static Double toRadians(Double degrees){
		return degrees * Math.PI / 180.0;
	}
	
	public static Double toDegrees(Double radians){
		return radians * 180.0 / Math.PI;
	}
	
	public static Double getBaseDistanceOfLng(Double lat){
		return BASE_DISTANCE_OF_LAT * Math.cos(toRadians(lat));
	}
	
	public static Double precisionConvert(Double data, String pattern){
		if(pattern != null){
			return Double.valueOf(new DecimalFormat(pattern).format(data));
		}else{
			return Double.valueOf(df.format(data));
		}
	}
	
	public static void main(String[] args) {
		Coordinate deviceCoordinate = new Coordinate(28.00, 112.00);
		Coordinate targetCoordinate = new Coordinate(28.12, 112.15); 
		
		Double result = CoordinateUtil.calcDistance(deviceCoordinate, targetCoordinate);
		System.out.println(precisionConvert(result, null));
		
		//测试数据
		//设备原点[28.00,112.00]
		//(28.0184,112.0146)	2500m
		//(28.0258,112.0204)	3500m
		//(28.0331,112.0263)	4500m
		//观望角度为35度角
		Coordinate zoomCoordinate1 = calcZoomCoordinateV1(15.0, 35.00, deviceCoordinate, targetCoordinate);
		System.out.println("v1:\t" + zoomCoordinate1);
		
		Coordinate zoomCoordinate0 = calcZoomCoordinateV0(15.0, 35.00, deviceCoordinate, targetCoordinate);
		System.out.println("v0:\t" + zoomCoordinate0);
		
		//验证结果
		System.out.println("v1:\t" + (result - CoordinateUtil.calcDistance(deviceCoordinate, zoomCoordinate1)));
		
		System.out.println("v0:\t" + (result - CoordinateUtil.calcDistance(deviceCoordinate, zoomCoordinate0)));
	}
}
